#include "graphsupport/solve/facilitylocation/PrimDualSolvContext.h"

#include <iostream>

#include "accumulsupport/AccumulSupport.h"

PrimDualSolvContext::PrimDualSolvContext(
	int citiesCount,
	int facilitiesCount,
	FacilitiesGraph::FacilityPriceType eps
)
	: fgraph( citiesCount, facilitiesCount )
	, cityPays4fclt( citiesCount )
	, cityPays4total( citiesCount )
	, specialSubgraph( facilitiesCount )
	, indepFacilities( facilitiesCount, false )
	, associatedFacility( citiesCount, -1 )
	, eps( eps )
{
	for ( int i = 0; i < citiesCount; i++ )
	{
		cityPays4fclt[i].resize( facilitiesCount );
	}
}

void PrimDualSolvContext::initFromGraph() // TODO: remove copy-paste
{
	int citiesCount = fgraph.getCitiesCount();
	int facilitiesCount = fgraph.getFacilitiesCount();

	cityPays4fclt.resize( citiesCount );
	for ( int i = 0; i < citiesCount; i++ )
	{
		cityPays4fclt[i].resize( facilitiesCount );
	}
	accumf::reinit( cityPays4total, citiesCount, 0 );
	specialSubgraph.init( facilitiesCount );
	accumf::reinit( indepFacilities, facilitiesCount, false );
	accumf::reinit( associatedFacility, citiesCount, -1 );
}

FacilitiesGraph::FacilityPriceType PrimDualSolvContext::getRoadPrice( int city, int fclt )
{
	return fgraph.getAdjacentMatrixArr()[city][fclt + fgraph.getCitiesCount()];
}

FacilitiesGraph::FacilityPriceType PrimDualSolvContext::getCityPays4road( int city, int fclt )
{
	return cityPays4total[city] - cityPays4fclt[city][fclt];
}

//TODO: improve performance
FacilitiesGraph::FacilityPriceType PrimDualSolvContext::getPayedForFacility( int facility )
{
	FacilitiesGraph::FacilityPriceType result = 0;
	for( int i = 0; i < fgraph.getCitiesCount(); i++ )
	{
		result += cityPays4fclt[i][facility];
	}
	return result;
}

FacilitiesGraph::FacilityPriceType PrimDualSolvContext::getCityPays4total( int city )
{
	return cityPays4total[city];
}

void PrimDualSolvContext::setCityPays4total( int city, FacilitiesGraph::FacilityPriceType cityPays )
{
	int facilitiesCount = fgraph.getFacilitiesCount();
	for ( int facility = 0; facility < facilitiesCount; facility++ )
	{
		if ( fgraph.existsBiPartEdge( city, facility ) )
		{
			FacilitiesGraph::FacilityPriceType roadPrice = getRoadPrice( city, facility );
			FacilitiesGraph::FacilityPriceType exceed = cityPays - roadPrice;
			if ( exceed > 0 )
			{
				if ( getPayedForFacility( facility ) > fgraph.facilitiesPrices[facility] + eps )
				{
					throw SolutionProcessException( "Can't set price for city" );
				}
				checkCheaperIncomeToFacility( city, facility, cityPays );
				cityPays4fclt[city][facility] = exceed;
			}
		}
	}
	cityPays4total[city] = cityPays;
}

void PrimDualSolvContext::checkCheaperIncomeToFacility( int city, int facility, FacilitiesGraph::FacilityPriceType cityPays )
{
	int citiesCount = fgraph.getCitiesCount();
	for ( int altCity = city + 1; altCity < citiesCount; altCity++ )
	{
		if (
			fgraph.getFSEdge( altCity, facility ) < fgraph.getFSEdge( city, facility ) - eps &&
			isXtremeEdge( altCity, facility )
		)
		{
//			std::cout << "\nwarning: city " << city << " pays for facility " <<
//				facility << " money " << cityPays << " but city " << altCity <<
//				" has extreme edge to the same facility with less cost( " <<
//				fgraph.getFSEdge( city, facility ) << " )";
			break;
		}
	}
}

bool PrimDualSolvContext::isXtremeEdge(int city, int facility)
{
	int facilityCount = fgraph.getFacilitiesCount();
	FacilitiesGraph::FacilityPriceType etalonEdge = fgraph.getFSEdge( city, facility );
	for ( int altFacility = 0; altFacility < facilityCount; altFacility++ )
	{
		if ( fgraph.getFSEdge( city, altFacility ) <  etalonEdge - eps )
		{
			return false;
		}
	}
	return true;
}
